package com.gitee.wsl.lang.character


/**
 * Modifying flags for a [Formattable] that modify the output
 */
enum class Flag(val char: Char) {
    /**
     * Left-justifies the result of [Formattable]
     */
    LEFT_JUSTIFY('-'),

    /**
     * Upper cases the result of [Formattable]
     */
    UPPERCASE('^'),

    /**
     * The result of [Formattable] should use a conversion-dependent alternative form.
     */
    ALTERNATE('#'),

    // numerics
    /**
     * Number results of [Formattable] will always include a sign, even when positive.
     */
    PLUS('+'),

    /**
     * Number results of [Formattable] will include a leading space when positive.
     */
    LEADING_SPACE(' '),

    /**
     * Number results of [Formattable] will be zero-padded.
     */
    ZERO_PAD('0'),

    /**
     * Number results of [Formattable] will include locale specific grouping separators.
     */
    GROUP(','),

    /**
     * Number results of [Formattable] will enclose negative numbers in parentheses.
     */
    PARENTHESES('('),

    // indexing
    /**
     * Causes the arguments of the previous [Formattable] to be used.
     */
    PREVIOUS('<'), ;

    companion object {
        fun parse(stringToMatch: String): Set<Flag> {
            val f = mutableSetOf<Flag>()
            for (c in stringToMatch) {
                val v = parse(c)
                if (f.contains(v)) throw StringFormatterException.DuplicateFormatFlagsException(v.toString())
                f.add(v)
            }
            return f
        }

        // parse those flags which may be provided by users
        private fun parse(c: Char): Flag = values().find { it.char == c } ?: throw StringFormatterException.UnknownFormatFlagsException(c.toString())
    }
}

@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
fun Set<Flag>.toString(): String {
    val sb = StringBuilder()
    this.forEach {
        sb.append(it.char)
    }
    return sb.toString()
}

/**
 * List of exceptions that may be thrown by a [StringFormatter].
 */
sealed class StringFormatterException(message: String?) : Exception(message) {
    data class MissingFormatArgumentException(val formatSpecified: String) : StringFormatterException("Format specifier '$formatSpecified'")
    data class MissingFormatWidthException(val specification: String) : StringFormatterException("The specification, $specification, misses a required width.")
    data class UnknownFormatConversionException(val conversion: String) : StringFormatterException("Conversion = '$conversion'")
    data class UnexpectedChar(val currentCharacter: Char) : StringFormatterException("Unexpected Char $currentCharacter")
    data class DuplicateFormatFlagsException(val flags: String) : StringFormatterException("Flags = '$flags'")
    data class UnknownFormatFlagsException(val flags: String) : StringFormatterException("Flags = '$flags'")
    data class NumberFormatException(val exception: kotlin.NumberFormatException) : StringFormatterException(exception.message)
    data class IllegalFormatWidthException(val width: Int) : StringFormatterException("$width")
    data class IllegalFormatPrecisionException(val precision: Int) : StringFormatterException("$precision")
    data class IllegalFormatConversionException(val currentChar: Char, val any: Any) : StringFormatterException("$currentChar != $any")
    data class IllegalFormatFlagsException(val flags: String) : StringFormatterException("An illegal set of flags, $flags, was supplied.")
    data class MalformedValue(val value: Any) : StringFormatterException("Malformed Value: $value")
    data class FormatFlagsConversionMismatchException(val flags: String, val currentChar: Char) : StringFormatterException("Conversion = $currentChar, Flags = $flags")
}